#include <types.h>
#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <console.h>
#include <kdebug.h>
#include <picirq.h>
#include <trap.h>
#include <clock.h>
#include <intr.h>
#include <pmm.h>
#include <vmm.h>
#include <ide.h>
#include <fs.h>
#include <swap.h>
#include <proc.h>
#include <sched.h>

int kern_init(void) __attribute__((noreturn));

static void mpmain(void);
static void bootothers(void);

int
kern_init(void) {
	extern char edata[], end[];
	memset(edata, 0, end - edata);
	mpinit();
	cons_init();				// init the console
	bootothers();
	mpmain();

//	pmm_init();					// init physical memory management

	/*
	pic_init();					// init interrupt controller
	idt_init();					// init interrupt descriptor table

	vmm_init();					// init virtual memory management
	sched_init();				// init scheduler
	proc_init();				// init process table
	sync_init();				// init sync struct

	ide_init();					// init ide devices
	swap_init();				// init swap
	fs_init();					// init fs

	clock_init();				// init clock interrupt
	intr_enable();				// enable irq interrupt

	cpu_idle();					// run idle process
	*/
}

static inline uint32_t
xchg(volatile uint32_t *addr, uint32_t newval)
{
  uint32_t result;
  
  // The + in "+m" denotes a read-modify-write operand.
  asm volatile("lock; xchgl %0, %1" :
               "+m" (*addr), "=a" (result) :
               "1" (newval) :
               "cc");
  return result;
}


// Common CPU setup code.
// Bootstrap CPU comes here from mainc().
// Other CPUs jump here from bootother.S.
static void
mpmain(void)
{
	const char *message = "(THU.CST) os is loading ...";
	if(cpunum() != mpbcpu()) {
		ksegment();
		lapicinit(cpunum());
	}
//	vmenable();				// turn on paging
	cprintf("cpu%d: starting\n", cpu->id);
//	idtinit();			 // load idt register
	xchg(&cpu->booted, 1);
//	scheduler();		 // start running processes
	cprintf("[%d]%s\n", strlen(message), message);
	debug_init();				// init debug registers
	while(1);
}

static void
bootothers(void)
{
	extern uint8_t _binary_bootother_start[], _binary_bootother_size[];
	uint8_t *code;
	struct cpu *c;
	char *stack;

	// Write bootstrap code to unused memory at 0x7000.  The linker has
	// placed the start of bootother.S there.
	code = (uint8_t *) 0x7000;
	memmove((uint8_t*)KADDR(code), _binary_bootother_start, (uint32_t)_binary_bootother_size);

	for(c = cpus; c < cpus+ncpu; c++){
		if(c == cpus+cpunum())  // We've started already.
			continue;

		// Fill in %esp, %eip and start code on cpu.
//			stack = kalloc();
		stack=(char*)(KADDR(0xfe2000));
		*(void**)(KADDR(code)-4) = stack + KSTACKSIZE;
		*(void**)(KADDR(code)-8) = mpmain;
		lapicstartap(c->id, (uint32_t)code);

		// Wait for cpu to finish mpmain()
		while(c->booted == 0);
	}
}
